#!/bin/bash

# Description:
#   Script to generate commit hashes from references generated by
#   `git show-ref` command.
#   Useful to recheck show-ref commits consistency.

# Usage:
#   git_gen_ref_commit_hashes.sh [<flags>] [//] [<show-ref-cmd-line>] [// [<hash-cmd> [<hash-cmd-line>]]]
#
#   <flags>:
#     -c
#       Print only commits with not equal hashes.
#     -p
#       Include print parents for each commit.
#   //:
#     Separator to stop parse flags or switch to next command line.
#   <show-ref-cmd-line>:
#     The command line passed to `git show-ref` command.
#   <hash-cmd>:
#     The hash command to execute for stdin pipe.
#     If not defined, then `sha1sum` is used.
#   <hash-cmd-line>:
#     The hash command line for `<hash-cmd>` command.

# Examples:
#   >
#   cd myrepo/path
#   git_gen_ref_commit_hashes.sh -cp // --tags
#
#   >
#   cd myrepo/path
#   git_gen_ref_commit_hashes.sh -cp // --tags // . -b
#
#   >
#   cd myrepo/path
#   git_gen_ref_commit_hashes.sh -cp // master // git hash-object --stdin

# Script both for execution and inclusion.
[[ -n "$BASH" ]] || return 0 || exit 0 # exit to avoid continue if the return can not be called

function git_gen_ref_commit_hashes()
{
  local flag="$1"

  local flag_only_not_equal_hashes=0
  local flag_print_parents=0

  while [[ "${flag:0:1}" == '-' ]]; do
    flag="${flag:1}"

    if [[ "${flag:0:1}" == '-' ]]; then
      echo "$0: error: invalid flag: \`$flag\`" >&2
      return 255
    fi

    while [[ -n "$flag" ]]; do
      if [[ "${flag//c/}" != "$flag" ]]; then
        flag_only_not_equal_hashes=1
        flag="${flag//c/}"
      elif [[ "${flag//p/}" != "$flag" ]]; then
        flag_print_parents=1
        flag="${flag//p/}"
      else
        echo "$0: error: invalid flag: \`${flag:0:1}\`" >&2
        return 255
      fi
    done

    shift

    flag="$1"
  done

  if [[ "$1" == '//' ]]; then
    shift
  fi

  local arg="$1"
  local showref_cmdline

  showref_cmdline=()

  while [[ -n "$arg" && "$arg" != '//' ]]; do
    showref_cmdline=("${showref_cmdline[@]}" "$arg")

    shift

    arg="$1"
  done

  if [[ "$1" == '//' ]]; then
    shift
  fi

  local hashcmd="$1"
  local hashcmdline=("${@:2}")

  if [[ -z "$hashcmd" || "$hashcmd" == '.' ]]; then
    hashcmd=("sha1sum")
  fi

  local line
  local hash ref

  local objtype refhash parenthash
  local hashsum
  local hashvalue suffix

  local print_empty_line num_parents

  local IFS

  IFS=$'\r\n'; for line in `git show-ref "${showref_cmdline[@]}"`; do # IFS - with trim trailing line feeds
    print_empty_line=0

    IFS=$'\t ' read -r refhash ref <<< "$line"

    objtype="$(git cat-file -t "$refhash")"
    hashsum="$(echo -ne "$objtype $(git cat-file -s "$refhash")\0$(git cat-file -p "$refhash")\n" | $hashcmd "${hashcmdline[@]}")"

    IFS=$'\t ' read -r hashvalue suffix <<< "$hashsum"

    if (( ! flag_only_not_equal_hashes )) || [[ "$hashvalue" != "$refhash" ]]; then
      echo "$hashvalue $refhash $objtype $ref"

      if (( flag_print_parents )); then
        num_parents=0

        IFS=$'\r\n'; for line in `git rev-parse "$refhash^@"`; do # IFS - with trim trailing line feeds
          (( num_parents++ ))

          IFS=$'\t ' read -r parenthash suffix <<< "$line"

          objtype="$(git cat-file -t "$parenthash")"
          hashsum="$(echo -ne "$objtype $(git cat-file -s "$parenthash")\0$(git cat-file -p "$parenthash")\n" | $hashcmd "${hashcmdline[@]}")"

          IFS=$'\t ' read -r hashvalue suffix <<< "$hashsum"

          if (( ! flag_only_not_equal_hashes )) || [[ "$hashvalue" != "$parenthash" ]]; then
            echo "$hashvalue $parenthash $objtype"
            echo
          else
            print_empty_line=1
          fi
        done

        if (( ! num_parents )); then
          print_empty_line=1
        fi
      else
        print_empty_line=1
      fi

      if (( print_empty_line )); then
        echo
      fi
    fi
  done
}

# shortcut
function git_gen_r_c_hs()
{
  git_gen_ref_commit_hashes "$@"
}

if [[ -z "$BASH_LINENO" || BASH_LINENO[0] -eq 0 ]]; then
  # Script was not included, then execute it.
  git_gen_ref_commit_hashes "$@"
fi
